package cn.csg.iotplatform.module.system.service.user;

import cn.csg.iotplatform.framework.common.exception.ServiceException;
import cn.csg.iotplatform.framework.common.pojo.CommonResult;
import cn.csg.iotplatform.module.common.util.RedisUtil;
import cn.csg.iotplatform.module.infra.api.config.ConfigApi;
import cn.csg.iotplatform.module.system.dal.mysql.user.AdminUserMapper;
import cn.csg.iotplatform.module.system.enums.ErrorCodeConstants;
import cn.csg.iotplatform.module.system.service.oauth2.OAuth2TokenService;
import com.alibaba.excel.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Objects;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import static cn.csg.iotplatform.framework.common.exception.util.ServiceExceptionUtil.exception;

@Component
@Slf4j
public class UserLoginManager {

    public static boolean USER_SINGLE_LOGIN = false;
    //用户登录失败有效期，单位：分钟
    public static Integer USER_LOGIN_FAIL_EXPIRE = 30;
    public static Integer USER_LOGIN_FAIL_COUNT = 5;
    public static Integer USER_LOGIN_FAIL_LOCK_TIME = 30;

    //系统配置key
    public static final String USER_SINGLE_LOGIN_KEY = "user_single_login";
    public static final String USER_LOGIN_FAIL_EXPIRE_KEY = "user_login_fail_expire";
    public static final String USER_LOGIN_FAIL_COUNT_KEY = "user_login_fail_count";
    public static final String USER_LOGIN_FAIL_LOCK = "user_login_fail_lock";
    @Resource
    private ConfigApi configApi;

    @Resource
    private OAuth2TokenService oAuth2TokenService;

    @Resource
    private RedisUtil redisUtil;

    @Resource
    private AdminUserMapper userMapper;

    @PostConstruct
    public void init() {
        ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor(r -> {
            Thread t = new Thread(r);
            t.setName("check-user-login-config-thread");
            t.setDaemon(true);
            return t;
        });
        service.scheduleWithFixedDelay(() -> checkUserLoginConfig(), 1, 1, TimeUnit.MINUTES);
    }

    private void checkUserLoginConfig() {
        try {
            CommonResult result = configApi.getConfigValue(USER_SINGLE_LOGIN_KEY);
            if (Objects.nonNull(result.getData())) {
                String value = (String) result.getData();
                USER_SINGLE_LOGIN = Boolean.parseBoolean(value);
            }

/*            result = configApi.getConfigValue(USER_LOGIN_FAIL_EXPIRE_KEY);
            if (Objects.nonNull(result.getData())) {
                String value = (String) result.getData();
                USER_LOGIN_FAIL_EXPIRE = Integer.parseInt(value);
            }

            result = configApi.getConfigValue(USER_LOGIN_FAIL_COUNT_KEY);
            if (Objects.nonNull(result.getData())) {
                String value = (String) result.getData();
                USER_LOGIN_FAIL_COUNT = Integer.parseInt(value);
            }*/
        } catch (Exception e) {
            log.error("checkUserLoginConfig error: {}", e.getMessage());
        }
        log.info("checkUserLoginConfig, value: {}", USER_SINGLE_LOGIN);
    }

    public void checkLoginPlace(Long userId) {
        if (USER_SINGLE_LOGIN) {
            oAuth2TokenService.removeAccessTokenByUserId(userId);
        }
    }

    public void loginLockUser(Long userId) {
        String lockKey = USER_LOGIN_FAIL_LOCK+ "_" + userId;
        if(redisUtil.get(lockKey) != null) {
            String validDayStr = userMapper.getPasswordValidDay(USER_LOGIN_FAIL_EXPIRE_KEY);
            if(StringUtils.isNotBlank(validDayStr)) {
                USER_LOGIN_FAIL_EXPIRE = Integer.parseInt(validDayStr);
            }

            validDayStr = userMapper.getPasswordValidDay(USER_LOGIN_FAIL_COUNT_KEY);
            if(StringUtils.isNotBlank(validDayStr)) {
                USER_LOGIN_FAIL_COUNT = Integer.parseInt(validDayStr);
            }
            validDayStr = userMapper.getPasswordValidDay(USER_LOGIN_FAIL_LOCK);
            if(StringUtils.isNotBlank(validDayStr)) {
                USER_LOGIN_FAIL_LOCK_TIME = Integer.parseInt(validDayStr);
            }

            throw exception(ErrorCodeConstants.AUTH_LOGIN_FAIL_TOO_MANY_TIMES,
                    USER_LOGIN_FAIL_EXPIRE, USER_LOGIN_FAIL_COUNT, USER_LOGIN_FAIL_LOCK_TIME);
        }
    };

    public void loginFail(Long userId) {
        String validDayStr = userMapper.getPasswordValidDay(USER_LOGIN_FAIL_EXPIRE_KEY);
        if(StringUtils.isNotBlank(validDayStr)) {
            USER_LOGIN_FAIL_EXPIRE = Integer.parseInt(validDayStr);
        }

        validDayStr = userMapper.getPasswordValidDay(USER_LOGIN_FAIL_COUNT_KEY);
        if(StringUtils.isNotBlank(validDayStr)) {
            USER_LOGIN_FAIL_COUNT = Integer.parseInt(validDayStr);
        }

        String lockKey = USER_LOGIN_FAIL_LOCK+ "_" + userId;
        if(redisUtil.get(lockKey) != null) {
            throw exception(ErrorCodeConstants.AUTH_LOGIN_FAIL_TOO_MANY_TIMES,
                    USER_LOGIN_FAIL_EXPIRE, USER_LOGIN_FAIL_COUNT, USER_LOGIN_FAIL_LOCK_TIME);
        }

        String key = String.format("user_login_fail_count_%s", userId);
        if (redisUtil.get(key) == null) {
            redisUtil.set(key, 1, USER_LOGIN_FAIL_EXPIRE * 60);
        } else {
            redisUtil.incr(key, 1);
        }
        Integer count = (Integer) redisUtil.get(key);
        if (count >= USER_LOGIN_FAIL_COUNT) {
            validDayStr = userMapper.getPasswordValidDay(USER_LOGIN_FAIL_LOCK);
            if(StringUtils.isNotBlank(validDayStr)) {
                USER_LOGIN_FAIL_LOCK_TIME = Integer.parseInt(validDayStr);
            }
            redisUtil.set(lockKey, 1, USER_LOGIN_FAIL_LOCK_TIME * 60);
            redisUtil.del(key);
            throw exception(ErrorCodeConstants.AUTH_LOGIN_FAIL_TOO_MANY_TIMES,
                    USER_LOGIN_FAIL_EXPIRE, USER_LOGIN_FAIL_COUNT, USER_LOGIN_FAIL_LOCK_TIME);
        }
    }
}
